#include "jni/jni_helpers.h"
#include "test.h"

#define JOW(rettype, name) extern "C" rettype JNIEXPORT JNICALL \
    Java_org_example_##name

namespace kjni
{
  class TestCallbackJava : public TestCallback
  {
  public:
    TestCallbackJava(JNIEnv *jni, jobject j_observer)
        : j_observer_global_(jni, j_observer),
          j_observer_class_(jni, GetObjectClass(jni, *j_observer_global_))
    {
    }

    virtual ~TestCallbackJava()
    {
    }

    void onCall(const std::string& str) override
    {
	    JNIEnv *jni = AttachCurrentThreadIfNeeded();
      ScopedLocalRefFrame local_ref_frame(jni);
      jmethodID m = GetMethodID(jni, *j_observer_class_, "onCall", "(Ljava/lang/String;)V");
      jstring j_str= JavaStringFromStdString(jni, str);
      jni->CallVoidMethod(*j_observer_global_,m,j_str);
      CHECK_EXCEPTION(jni) << "error during call onCall";
    }

  private:
    const ScopedGlobalRef<jobject> j_observer_global_;
    const ScopedGlobalRef<jclass> j_observer_class_;
  };


  JOW(jlong, Test_nativeCreateCallback)
    (JNIEnv *jni, jclass, jobject j_observer)
  {
    return (jlong) new TestCallbackJava(jni, j_observer);
  }

  JOW(void, Test_nativeFreeCallback)
  (JNIEnv *, jclass, jlong j_p)
  {
    TestCallbackJava *p = reinterpret_cast<TestCallbackJava *>(j_p);
    delete p;
  }

  static TestCallbackJava *callbackFromJava(jlong j_p)
  {
    return reinterpret_cast<TestCallbackJava *>(j_p);
  }

  static Test *apiFromJava(jlong j_p)
  {
    return reinterpret_cast<Test *>(j_p);
  }

  JOW(jlong, Test_nativeCreate)
    (JNIEnv *jni, jclass, jlong j_observer)
  {
    TestCallbackJava *obs = callbackFromJava(j_observer);
    Test *test = new Test(obs);
    return jlongFromPointer(test);
  }

  JOW(void, Test_nativeFree)
  (JNIEnv *, jclass, jlong j_p)
  {
    delete reinterpret_cast<Test *>(j_p);
  }

  JOW(void, Test_nativeSetStr)
  (JNIEnv *jni, jclass, jlong j_p,jstring j_str)
  {
    Test *test = apiFromJava(j_p);
    test->setStr(JavaToStdString(jni,j_str));
  }

  JOW(jstring, Test_nativeGetStr)
  (JNIEnv *jni, jclass, jlong j_p)
  {
    Test *test = apiFromJava(j_p);
    return JavaStringFromStdString(jni,test->getStr());
  }

  JOW(void, Test_nativeTestCallback)
  (JNIEnv *jni, jclass, jlong j_p)
  {
    Test *test = apiFromJava(j_p);
    test->testCallback();
  }

} // namespace kjni
